#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <stdio.h>

typedef enum jstatus
{
   FOREGROUND, BACKGROUND, STOPPED, DONE, TERMINATED
}job_status_t;

typedef struct job_struct
{
   char *command;
   pid_t pgid;
   int job;
   job_status_t jobstat;
   struct job_struct *next_job;
}joblist_t;

static joblist_t *job_head = NULL;
static joblist_t *job_tail = NULL;
static joblist_t *trav_ptr;
static job_status_t temp;
static int numJobs = 1000;

void show_jobs(void);

/**
 * Adds the specified job to the list
 * pgid - processes group id
 * cmd - command string for the job
 * status - value FOREGROUND or BACKGROUND
 * Returns job number or -1 if unsuccessful
 */
int add_list(pid_t pgid, char *cmd, job_status_t status)
{
   //Create a new joblist_t item
   joblist_t *newjob;
   
   //Allocate memory, return -1 if unable to allocate
   if((newjob = (joblist_t *)(malloc(sizeof(joblist_t) +
      strlen(cmd) + 1))) == NULL)
   { 
      fputs("Error allocating memory for job.", stdout);
      return -1;
   }
   
   newjob->command = malloc(sizeof(cmd)); 
   strcpy(newjob->command, cmd);
   newjob->pgid = pgid;
   newjob->jobstat = status;
   newjob->job = numJobs;
   
   newjob->next_job = NULL;

   //If the list is empty, add new job to the head
   if(job_head == NULL)
   { 
      job_head = newjob;
   }
   //else, add the job to the end of the list
   else
   {  
      job_tail->next_job = newjob;
   }
   job_tail = newjob;
   
   numJobs++;
   return newjob->job;
}

/**
 * Removes the node corresponding to the specified job from
 * the list. 
 * Returns the job number if the node is successfully deleted
 * else returns -1 on failure
 * TODO: Issues with removing jobs that do not exist
 */
int delete_job(int job)
{
   joblist_t *ptr1, *ptr2;
   ptr1 = job_head;
   ptr2 = job_head->next_job;
   
   /* if the list is empty, return -1 */
   if(job_head == NULL && job_head == NULL)
   { 
      return -1;
   }
   
   /* If job is first in list, remove */
   if(job_head->job == job)
   {
      free(job_head);
      job_head = job_head->next_job;
      return job;     
   }
   
   /* If the job is at the end of the list, remove */
   else if(job_tail->job == job)
   {   
      printf("\nJob at end of list.");
      while(ptr2->next_job != NULL)
      {
         ptr1 = ptr2;
         ptr2 = ptr1->next_job;
      }
      free(ptr1->next_job);
      ptr1->next_job = NULL;
      job_tail = ptr1;         
      return job;
   }
   
   /* Else, search within the list */
   else
   {     
      printf("\nJob within the list");
      while(ptr2->next_job != NULL)
      {
         ptr1 = ptr2;
         ptr2 = ptr1->next_job;
         if(ptr2->job == job)
         {
            free(ptr1->next_job);
            ptr1->next_job = ptr2->next_job;
            return job;
         }
      }
      /* If job not found return -1 */
      return -1;
   }
} /* end delete_list */
 
/**
 * Outputs a list of jobs with its status.
 * Format [job]   status   pgid   cmd
 */
void show_jobs(void)
{
   int counter = 1;
   reset_ptr();
   
   printf("\n\njob\tstatus\t\t pgid \t\tcommand\n");
   printf("=====\t==========\t=======\t\t===========\n");
   while(trav_ptr != NULL)
   {
      printf("%d   \t", trav_ptr->job);
      printf("%d     \t\t", trav_ptr->jobstat);
      printf("%d     \t", trav_ptr->pgid);
      printf("%s   \n", trav_ptr->command);
      
      trav_ptr = trav_ptr->next_job;
   }   
} /* end show_jobs */

/**
 * Sets the value of the node of the corresponding job
 * to either FOREGROUND or BACKGROUND.
 * Returns 0 on success and -1 on failure
 */
int set_status(int job, job_status_t status)
{
   reset_ptr();
   
   while(trav_ptr != NULL)
   {
      if(trav_ptr->job == job)
      {
         trav_ptr->jobstat = status;
         return 0;
      }
      trav_ptr = trav_ptr->next_job;
   }
   return -1;
}

/**
 * Returns the status value associate with the specified job.
 * Returns 0 on success and -1 on failure
 */
int get_status(int job, job_status_t *pstatus)
{
   return -1;
}

/**
 * Returns the process group ID of the specified job. 
 * If the job doesn't exist, returns 0
 */
pid_t get_process(int job)
{
   reset_ptr();
   pid_t pgid = -1;
   
   while(trav_ptr != NULL)
   {
      if(trav_ptr->job == job){
         pgid = trav_ptr->pgid;
      }
      trav_ptr = trav_ptr->next_job;
   }
   return pgid;
}

/**
 * Scans the job list for the largest job number currently
 * in the list. 
 * Returns the largest job number if thre are any nodes on 
 * the list or 0 if the list is empty.
 */
int get_largest_job_number(void)
{
   reset_ptr();
   int highestJob = 0;
   
   while(trav_ptr != NULL)
   {
      if(trav_ptr->job > highestJob)
         highestJob = trav_ptr->job;
      trav_ptr = trav_ptr->next_job;
   }
   return highestJob;
}


/**
 * Set the traversal pointer to contain the address of job_head.
 * If job_head is NULL, return -1 indicating an empty list.
 * Otherwise, return 0.
 */
int reset_ptr(void)
{
   trav_ptr = job_head;
   if(job_head == NULL)
      return -1;
   else return 0;
}